package com.kinghunt.misp.common.utils.easyExcel.order;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 导出excel 表格 工具类
 * @author xia.jiangtao
 * @date 2023/12/3 16:18
 */
public class EasyExcelOrderUtil {
    //在这里新增了一个ThreadLocal类变量，用以存储一个自适应列宽的策略
    private static final ThreadLocal<LongestMatchColumnWidthStyleStrategy> matchStrategy = new ThreadLocal();

    public void createExcel(HttpServletResponse response, String fileName, ExportOrderCommon params, List<String> fields)
            throws IOException  {
        // 设置下载信息
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        //fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        //获取公共部分数据
        ExportOrderCommon.OrderInfoData orderInfoData = params.getOrderInfoData();
        //获取列表部分数据
        List<? extends ExportOrderCommon.OrderBookList> orderBookList = params.getOrderBookList();
        //获取列表部分数据的类对象
        Class<? extends ExportOrderCommon.OrderBookList> listDataClass = orderBookList.get(0).getClass();
        //每次构建一个新的Excel文件时，新建一个自适应列宽策略对象，并存入ThreadLocal中
        LongestMatchColumnWidthStyleStrategy matchWidthStrategy = new LongestMatchColumnWidthStyleStrategy();
        matchStrategy.set(matchWidthStrategy);
        //构建EasyExcel Writer对象
        ExcelWriter writer = null;
        try {
            writer = EasyExcel.write(response.getOutputStream(), listDataClass)//指定写入的流，以及需要EasyExcel自带动态生成的类的类对象
                    .excelType(ExcelTypeEnum.XLSX).build();
            WriteSheet sheet = EasyExcel
                    .writerSheet(fileName)//指定写入的sheet
                    .needHead(false)//是否需要head，也就是每一个字段对应的字段名，这里为不需要，我们需要EasyExcel去生成字段名的地方只有列表数据部分
                    .build();
            //使用一个计数器记录当前已经写了几个表格
            AtomicInteger tableNoCounting = new AtomicInteger(1);
            //需要知道列数的最大值是多少
            int maxColumn = fields.size();
            this.buildCommon(maxColumn, orderInfoData, sheet, writer, tableNoCounting);
            this.buildList(orderBookList, sheet, writer, tableNoCounting, fields);
        } finally {
            assert writer != null;
            // 关闭流
            writer.finish();
            matchStrategy.remove();
        }
    }

    public void export(OutputStream os, ExportOrderCommon params, List<String> fields) {
        //获取公共部分数据
        ExportOrderCommon.OrderInfoData orderInfoData = params.getOrderInfoData();
        //获取列表部分数据
        List<? extends ExportOrderCommon.OrderBookList> orderBookList = params.getOrderBookList();
        //获取列表部分数据的类对象
        Class<? extends ExportOrderCommon.OrderBookList> listDataClass = orderBookList.get(0).getClass();
        //每次构建一个新的Excel文件时，新建一个自适应列宽策略对象，并存入ThreadLocal中
        LongestMatchColumnWidthStyleStrategy matchWidthStrategy = new LongestMatchColumnWidthStyleStrategy();
        matchStrategy.set(matchWidthStrategy);

        //构建EasyExcel Writer对象
        ExcelWriter writer = null;

        try {
            writer = EasyExcel.write(os, listDataClass)//指定写入的流，以及需要EasyExcel自带动态生成的类的类对象
                    .excelType(ExcelTypeEnum.XLSX).build();
            WriteSheet sheet = EasyExcel
                    .writerSheet("sheet1")//指定写入的sheet
                    .needHead(false)//是否需要head，也就是每一个字段对应的字段名，这里为不需要，我们需要EasyExcel去生成字段名的地方只有列表数据部分
                    .build();
            //使用一个计数器记录当前已经写了几个表格
            AtomicInteger tableNoCounting = new AtomicInteger(1);
            //需要知道列数的最大值是多少
            int maxColumn = fields.size();
            this.buildCommon(maxColumn, orderInfoData, sheet, writer, tableNoCounting);
            this.buildList(orderBookList, sheet, writer, tableNoCounting, fields);
        } finally {
            assert writer != null;
            // 关闭流
            writer.finish();
            matchStrategy.remove();
        }
    }

    /**
     * 属性实体类
     */
    private static class ExcelField {
        //属性名
        private String fieldName;
        //Excel中显示名
        private String showName;
        //排序
        private int order;
        //属性值
        private Object value;

        public String getFieldName() {
            return fieldName;
        }

        public void setFieldName(String fieldName) {
            this.fieldName = fieldName;
        }

        public String getShowName() {
            return showName;
        }

        public void setShowName(String showName) {
            this.showName = showName;
        }

        public int getOrder() {
            return order;
        }

        public void setOrder(int order) {
            this.order = order;
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }
    }
    /**
     * 构建公共部分
     */
    private void buildCommon(int maxColumn, ExportOrderCommon.OrderInfoData commonTable, WriteSheet sheet, ExcelWriter writer, AtomicInteger tableNoCounting) {
        if (!Objects.isNull(commonTable)) {
            //获取公共数据的类对象
            Class<?> commonDataClass = commonTable.getClass();
            //通过类对象获取该类中的所有属性
            List<Field> fields = this.getAllField(commonDataClass);
            List<ExcelField> fieldList = new ArrayList<>();
            try {
                for (Field field : fields) {
                    //如果在Maven打包时报错，Spring项目中可以替换为Spring中的BeanUtils.getPropertyDescriptor（）
                    PropertyDescriptor pd = new PropertyDescriptor(field.getName(), commonDataClass);
                    Assert.notNull(pd, "");
                    //反射获取读方法
                    Method readMethod = pd.getReadMethod();
                    //读到属性值
                    Object fieldValue = readMethod.invoke(commonTable);
                    //获取属性注解
                    ExcelProperty property = field.getAnnotation(ExcelProperty.class);
                    //获取Excel显示名称
                    String excelFieldName = property.value()[0];
                    //获取Excel中排序
                    int excelFieldOrder = property.order();
                    //构建对象
                    ExcelField excelField = new ExcelField();
                    excelField.setFieldName(field.getName());
                    excelField.setShowName(excelFieldName);
                    excelField.setOrder(excelFieldOrder);
                    excelField.setValue(fieldValue);
                    fieldList.add(excelField);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            //根据Order排序
            fieldList.sort(Comparator.comparingInt(ExcelField::getOrder));
            int count = fieldList.size();
            //计算一行显示属性的个数，除以3是因为一个属性需要属性名--属性值--空字符串三个单元格
            int lineCount = maxColumn / 3;
            //计算行数
            int rows = (count + lineCount - 1) / lineCount;
            List<Object> cellList = new ArrayList<>();
            List<List<Object>> rowList = new ArrayList<>();
            //自定义样式
            WriteCellStyle cellStyle = new WriteCellStyle();
            //水平靠左
            cellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
            //遍历所有行，一行作为一个表
            for (int row = 0; row < rows; ++row) {
                WriteTable table = EasyExcel.writerTable(tableNoCounting.getAndIncrement())
                        .needHead(Boolean.FALSE)
                        .registerWriteHandler(
                                new HorizontalCellStyleStrategy(cellStyle, cellStyle)
                        )
                        //添加自适应列宽策略
                        .registerWriteHandler(matchStrategy.get())
                        .build();
                //构建List<List<String>>类型的数据给EasyExcel导出
                for (int i = 0; i < lineCount && row * lineCount + i < count; ++i) {
                    ExcelField field = fieldList.get(row * lineCount + i);
                    cellList.add(field.getShowName() + ":");
                    cellList.add(field.getValue());
                    cellList.add("");
                }

                rowList.add(cellList);
                //指定写入的sheet和table
                writer.write(rowList, sheet, table);
                cellList.clear();
                rowList.clear();
            }
        }
    }

    /**
     * 构建列表部分
     */
    private void buildList(List<? extends ExportOrderCommon.OrderBookList> listTable, WriteSheet sheet, ExcelWriter writer, AtomicInteger tableNoCounting, List<String> fields) {
        //自定义样式
        WriteCellStyle headStyle = new WriteCellStyle();
        //设置header背景颜色为透明
        headStyle.setFillForegroundColor(IndexedColors.AUTOMATIC.getIndex());
        //水平居中
        headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        //上下左右四个边框
        headStyle.setBorderBottom(BorderStyle.THIN);
        headStyle.setBorderTop(BorderStyle.THIN);
        headStyle.setBorderLeft(BorderStyle.THIN);
        headStyle.setBorderRight(BorderStyle.THIN);
        WriteFont writeFont = new WriteFont();
        //字体加粗
        writeFont.setBold(Boolean.TRUE);
        //字号
        writeFont.setFontHeightInPoints((short) 12);
        headStyle.setWriteFont(writeFont);
        WriteCellStyle contentStyle = new WriteCellStyle();
        //内容上下左右四个边框
        contentStyle.setBorderBottom(BorderStyle.THIN);
        contentStyle.setBorderTop(BorderStyle.THIN);
        contentStyle.setBorderLeft(BorderStyle.THIN);
        contentStyle.setBorderRight(BorderStyle.THIN);
        //水平居中
        contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        WriteTable table = EasyExcel.writerTable(tableNoCounting.getAndIncrement())
                .needHead(Boolean.TRUE)//需要Header
                //添加自适应列宽策略
                .registerWriteHandler(matchStrategy.get())
                .registerWriteHandler(new HorizontalCellStyleStrategy(headStyle, contentStyle))//传入自定义样式
                .includeColumnFiledNames(fields)//选择需要哪些属性
                .build();
        writer.write(listTable, sheet, table);
    }

    //获取该类的所有属性，包括父类中不重名的属性
    private List<Field> getAllField(Class<?> clazz) {
        List<Field> resultList = new ArrayList<>();
        for (List<String> fieldNameList = new ArrayList<>();
             clazz != null && !clazz.getName().toLowerCase().equals(ExportOrderCommon.class.getName());
             clazz = clazz.getSuperclass()) {
            List<Field> subFields = Arrays.asList(clazz.getDeclaredFields());
            List<Field> list = subFields.stream()
                    .filter((f) -> !fieldNameList.contains(f.getName()))
                    .collect(Collectors.toList());
            List<String> nameList = list.stream().map(Field::getName).collect(Collectors.toList());
            resultList.addAll(list);
            fieldNameList.addAll(nameList);
        }
        return resultList;
    }
}